import constants from '@/constants';
import { halt } from '@/utils';
import { InjectRedis } from '@liaoliaots/nestjs-redis';
import { HttpException, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import Redlock, { RedlockAbortSignal, Settings } from 'redlock';

@Injectable()
export class RedlockService {
  private readonly redlock: Redlock;

  constructor(@InjectRedis() private readonly redis: Redis) {
    this.redlock = new Redlock([this.redis], {
      // The expected clock drift; for more details see:
      // http://redis.io/topics/distlock
      driftFactor: 0.01, // multiplied by lock ttl to determine drift time

      // The max number of times Redlock will attempt to lock a resource
      // before erroring.
      retryCount: 0,

      // the time in ms between attempts
      retryDelay: 200, // time in ms

      // the max time in ms randomly added to retries
      // to improve performance under high contention
      // see https://www.awsarchitectureblog.com/2015/03/backoff.html
      retryJitter: 200, // time in ms

      // The minimum remaining time on a lock before an extension is automatically
      // attempted with the `using` API.
      automaticExtensionThreshold: 500, // time in ms
    });
    this.redlock.on('error', (error) => {
      console.error('Redlock error:', error);
    });
  }

  async using(
    resources: string[],
    duration: number,
    settings?: Partial<Settings>,
    routine?: (signal: RedlockAbortSignal) => Promise<unknown>,
  ) {
    try {
      return await this.redlock.using(
        resources,
        duration,
        {
          retryCount: 0,
          ...settings,
        },
        routine,
      );
    } catch (e) {
      if (e instanceof Error && !(e instanceof HttpException)) {
        console.error(e.message, e);
        return halt(constants.isDev ? e.message : '业务繁忙, 请稍后重试');
      }
      return halt(e?.response ?? '业务处理繁忙, 请稍后重试');
    }
  }
}
